package com.erp.config;

import com.alibaba.fastjson.JSON;
import com.erp.controller.UserController;
import com.erp.dto.TokenData;
import com.erp.pojo.Log;
import com.erp.pojo.User;
import com.erp.service.LogService;
import com.erp.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//基于Spring AOP（面向切面编程）的登录检查切面类，用于在用户访问某些接口时验证是否已经登录

//@Aspect：声明该类是一个切面类，用于定义切面逻辑（如拦截方法、处理逻辑等）。
//@Component：将该类注册为Spring容器管理的组件，确保Spring能够扫描并加载它。
@Aspect
@Component
@Order(100)
public class LoginCheckAspect {
//HttpServletRequest request：用于获取当前HTTP请求的相关信息，例如请求路径、请求头等。
//HttpServletResponse response：用于操作HTTP响应，例如设置响应状态码、写入响应内容等。
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;
    @Autowired
    LogService logService;
    @Autowired
    private UserService userService;


    // 拦截所有带有 GetMapping、PostMapping、PutMapping、DeleteMapping 注解的方法
    //@Around：定义了一个环绕通知，表示在目标方法执行前后都会执行切面逻辑。
    //@annotation(...)：用于指定拦截的条件。这里拦截了所有带有@GetMapping、@PostMapping、@PutMapping、@PostMapping和@DeleteMapping注解的方法，
    // 即拦截了所有通过HTTP请求访问的接口方法。
    @Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.DeleteMapping)")

    //ProceedingJoinPoint joinPoint：参数类型为ProceedingJoinPoint，表示当前的连接点（即被拦截的方法）。
    // 通过调用joinPoint.proceed()可以继续执行目标方法。


    //quals方法的默认实现是通过比较对象的内存地址来判断两个对象是否相等。换句话说，如果没有重写equals方法，那么它会检查两个引用是否指向同一个对象。
    //equals与==的区别
    //==：
    //用于比较两个引用是否指向同一个对象（即内存地址是否相同）。
    //对于基本数据类型（如int、double等），==用于比较值是否相等。
    //equals：
    //用于比较两个对象的内容是否相等。
    //默认情况下比较内存地址，但可以通过重写equals方法来比较对象的实际内容
    public Object checkLogin(ProceedingJoinPoint joinPoint) throws Throwable {
        String requestURL = request.getRequestURI();
        if("/userLogin".equals(requestURL)){
            return joinPoint.proceed();
        }

        String auth = request.getHeader("authorization");
        TokenData tokenData = UserController.map.get(auth);
        if(auth==null || auth.isEmpty()){
            Map<String,Object> map = new HashMap<>();
            map.put("code", 403);
            map.put("message", "未登录");
            response.getWriter().print(JSON.toJSONString(map));
            return null;
        }
        auth = auth.replace("Bearer ", "");
        boolean login = UserController.map.containsKey(auth);
        if(!login){
            Map<String,Object> map = new HashMap<>();
            map.put("code", 403);
            map.put("message", "未登录");
            response.getWriter().print(JSON.toJSONString(map));
            return null;
        }
        // 从 joinPoint 中获取方法参数并转换为 JSON 字符串
        Object[] args = joinPoint.getArgs();
        String requestDataJson = com.alibaba.fastjson.JSON.toJSONString(args);

        if(auth==null || auth.isEmpty()){
            Map<String,Object> map = new HashMap<>();
            map.put("code", 403);
            map.put("message", "未登录");
            response.getWriter().print(JSON.toJSONString(map));
            return null;
        }
        Integer uid=tokenData.getUserId();
        User user=  userService.getById(uid);
        Log log = new Log();
        log.setUid(uid);
        log.setData(requestDataJson);
        log.setUrl(requestURL);
        log.setCreatTime(new Date());
        log.setUname(user.getUname());
        logService.save(log);



        //从请求头中获取authorization字段，通常用于存储用户的认证信息（如Token）。
        //如果authorization字段为空，说明用户未登录。
        //创建一个包含错误信息的Map对象，设置code为403（表示未授权），message为“未登录”。
        //将Map对象序列化为JSON字符串，并通过HttpServletResponse写入响应体，返回给客户端。
        //返回null，表示不再执行目标方法。


        //将authorization字段中的Bearer 前缀去掉，通常Token是以Bearer 开头的。
        //通过UserController.map.containsKey(auth)检查Token是否存在于用户登录信息的Map中（假设UserController.map是一个存储用户登录状态的Map）。
        //如果Token不存在，说明用户未登录，返回403错误信息。
        //如果Token存在，说明用户已登录，继续执行目标方法
        tokenData.setLastAccessTime(new Date());
        return joinPoint.proceed();
    }
}